package com.tjn.synchronized_;

/**
 * @author 陶镓妮
 * @version 1.0
 * 用synchronized关键字实现线程同步，解决买票的问题
 */
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

//        SellTicket02 sellTicket02 = new SellTicket02();
//        Thread thread01 = new Thread(sellTicket02);
//        Thread thread02 = new Thread(sellTicket02);
//        Thread thread03 = new Thread(sellTicket02);
//        thread01.start();
//        thread02.start();
//        thread03.start();

        //两种方式都会造成票重复出售或者超售
        //解决：
        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();//第 1 个线程-窗口
        new Thread(sellTicket03).start();//第 2 个线程-窗口
        new Thread(sellTicket03).start();//第 3 个线程-窗口
    }
}

//使用synchronized实现线程同步

class SellTicket03 implements Runnable{
    private int ticket = 100;//用Runnable接口实现，不需要static关键字，因为在创建每个线程时，引入的创建的对象是一样的，所以用的同一个ticket
    private boolean loop = true;
    Object object = new Object();

    //如果这个同步方法是静态的，那么这个同步方法就会锁住整个类，而不是锁住对象
    public synchronized static void m1(){//同步方法，同一时刻，只有一个线程可以进入这个方法
    }
    //如果在一个静态方法中实现一个同步代码块，那么synchronized后的括号里面应该写类名.class，就不能再写对象或this了
    public static void m2(){
        synchronized(SellTicket03.class){
            System.out.println("m2");
        }
    }


    public /*synchronized*/ void sell(){//同步方法，同一时刻，只有一个线程可以进入这个方法
        synchronized (/*this*/object){//this表示当前对象，即SellTicket03对象,也可以写其他对象,但是必须是这三个线程操作的同一个对象
                                    //三个线程操作的是同一个object
            if(ticket <= 0){
                System.out.println("票卖完了");
                loop = false;
                return;
            }
            System.out.println("窗口"+ Thread.currentThread().getName() + "卖出一张票,还有" + (--ticket) + "张票");
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }


    }
    @Override
    public void run() {
        while(loop){
            sell();
        }
    }
}


//用Thread类来实现买票功能
class SellTicket01 extends Thread{
    private static int ticket = 100;//用静态的变量，就可以保证多个线程享用同一个 ticket

    //要是这种情况后面写个this想要实现同步是不行的，因为这个类继承的是Thread类，创建对象时创建的几个线程不是同一个对象
    //就是必须保证多个线程锁的是同一个对象
//    public void m1(){
//        synchronized (this){
//            System.out.println("hello");
//        }
//    }

    @Override
    public void run() {
        while(true){
            System.out.println("窗口"+ Thread.currentThread().getName() + "卖出一张票,还有" + (--ticket) + "张票");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(ticket <= 0){
                System.out.println("票卖完了");
                break;
            }
        }
    }
}

//用Runnable接口来实现买票功能
class SellTicket02 implements Runnable{
    private int ticket = 100;//用Runnable接口实现，不需要static关键字，因为在创建每个线程时，引入的创建的对象是一样的，所以用的同一个ticket
    @Override
    public void run() {
        while(true){
            System.out.println("窗口"+ Thread.currentThread().getName() + "卖出一张票,还有" + (--ticket) + "张票");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(ticket <= 0){
                System.out.println("票卖完了");
                break;
            }
        }
    }
}
